package com.lx.inter.query.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.lx.common.utils.CommonClassUtil;
import com.lx.constant.enums.SegmentEnum;
import com.lx.constant.execption.LambdaCheckException;
import com.lx.constant.segment.query.ColumnSegment;
import com.lx.inter.query.ResultConvertFunction;
import com.lx.wrapper.AbstractWrapper;
import lombok.Getter;

import javax.persistence.Column;
import javax.persistence.JoinColumn;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 默认H/SQL查询结果转换器
 *
 * @author lx
 * @create 2023/2/25 16:47
 **/
public class DefaultResultConvertFunction<Wrapper extends AbstractWrapper<?, Wrapper>> implements ResultConvertFunction<Wrapper> {
    /**
     * 日志记录
     */
    private final Log log = LogFactory.get(DefaultResultConvertFunction.class);
    /**
     * 委托Wrapper
     */
    private final Wrapper entrustWrapper;

    public DefaultResultConvertFunction(Wrapper entrustWrapper) {
        this.entrustWrapper = entrustWrapper;
    }

    /**
     * 结果转换类
     */
    @Getter
    private Class<?> convertClass;

    @Override
    public Wrapper setConvertClass(Class<?> convertClass) {
        this.convertClass = convertClass;
        return entrustWrapper;
    }

    @Override
    public <Response> Response convertToResponse(Class<Response> resultClass, Object hibernateRes) {
        if (hibernateRes == null || resultClass == null) {
            return null;
        }
        return convertToResponse0(resultClass, null, getFieldListByClassAndColumn(resultClass), hibernateRes);
    }

    @Override
    public <Response> Response convertToResponse(Response response, Object hibernateRes) {
        if (hibernateRes == null || response == null) {
            return null;
        }
        Class<Response> responseClass = (Class<Response>) response.getClass();

        return convertToResponse0(responseClass, response, getFieldListByClassAndColumn(responseClass), hibernateRes);
    }

    @Override
    public <Response> List<Response> convertToResponseList(List<Object> hibernateResList, final Class<Response> resultClass) {
        if (CollectionUtil.isEmpty(hibernateResList)) {
            return new ArrayList<>();
        }
        if (resultClass == null) {
            throw new LambdaCheckException("生成Result对象时,Result对应的Class类型不能为空!");
        }
        //获取所有的可查询列名
        List<Field> columnFieldList = getFieldListByClassAndColumn(resultClass);

        //优化:防止重复裂变数组
        List<Response> resList = new ArrayList<>(hibernateResList.size());
        hibernateResList.stream().forEach(dataArray -> {
            resList.add(convertToResponse0(resultClass, null, columnFieldList, dataArray));
        });
        return resList;
    }


    /**
     * * 将Hibernate数据根据结果类转换为结果数据;
     * * 转换顺序如下
     * * 1.结果类类型同当前数据类型一致
     * * 2.结果类为包装或原始数据类型
     * * 3.Map类型处理
     * * <p>
     * * 6.默认处理(常规转换)
     *
     * @param responseClass      结果对象实例类
     * @param response           结果对象实例,如未传入则有实例类实现
     * @param resColumnFieldList 结果属性列表,来源于[结果类属性列表]同[已查列值属性列表]交集部分
     * @param dataObject         结果数据
     * @param <Response>
     */
    protected <Response> Response convertToResponse0(final Class<Response> responseClass, Response response, List<Field> resColumnFieldList, final Object dataObject) {
        if (dataObject == null) {
            return null;
        }

        //1.当返回类类型同参数类型一致,则无需转换.
        if (Objects.equals(responseClass, dataObject.getClass())) {
            return (Response) dataObject;
        }

        //2.提前捕获结果类是否为原始类型或包装类型,则结束后续转换
        if (ClassUtil.isBasicType(responseClass)) {
            //如果是包装类型,则将结果参数提前转换.
            Object primitiveValue = CommonClassUtil.convertToPrimitiveValue(responseClass, dataObject);
            if (primitiveValue == null) {
                throw new LambdaCheckException("当结果转换类为原始类型=[" + responseClass.getName() + "]时,无法正确转换成功,转换值=[" + dataObject + "]");
            }
            return (Response) primitiveValue;
        }

        //3.转换类为Map及其子类时,需要特殊处理.
        if (Map.class.isAssignableFrom(responseClass)) {
            //单个对象转换时数据对象无法重置为对象数组，在此处需要映射转换
            Object[] dataArray = dataObject instanceof Object[] ? (Object[]) dataObject : new Object[]{dataObject};
            //获取实例类
            Response instance = response == null ? CommonClassUtil.richNewInstance(responseClass) : response;
            Map rrr = (Map) instance;
            //获取所有的列属性.
            List<ColumnSegment> segments = entrustWrapper.getSegmentsByEnum(SegmentEnum.COLUMN);

            for (int i = 0, length = dataArray.length; i < length; i++) {
                Object data = dataArray[i];
                if (data == null) {
                    continue;
                }
                ColumnSegment columnSegment = segments.get(i);
                String key;
                if (columnSegment != null && (key = columnSegment.generatorResFieldName()) != null) {
                    rrr.put(key, data);
                }
            }
            return instance;
        }

        //6.单个对象转换时数据对象无法重置为对象数组，在此处需要映射转换
        if (CollectionUtil.isEmpty(resColumnFieldList)) {
            return null;
        }

        Object[] dataArray = dataObject instanceof Object[] ? (Object[]) dataObject : new Object[]{dataObject};
        //获取实例类
        Response instance = response == null ? CommonClassUtil.richNewInstance(responseClass) : response;

        for (int i = 0, length = dataArray.length; i < length; i++) {
            Object data = dataArray[i];
            if (data == null) {
                continue;
            }
            Field columnField = resColumnFieldList.get(i);
            if (columnField == null) {
                //不存在对应的属性对象
                continue;
            }
            try {
                //获取列名语句对象
                ReflectUtil.setFieldValue(instance, columnField, data);
            } catch (Exception e) {
                log.warn("设置结果类=[" + responseClass + "]中列属性=[" + columnField.getName() + "]对应的值失败,设置值=[" + data + "],异常原因:" + e.fillInStackTrace());
            }
        }
        return instance;
    }

    /**
     * 根据类和现有Column列表达式获取对应的属性对象列表,支持以下情况转换
     * 1. 语句查询列名存在别名
     * 2. 语句查询手动输入列名
     * 3. 结果类存在@Column或@JoinColumn注解,可通过列名字符串映射
     *
     * @param clazz
     * @return
     */
    private List<Field> getFieldListByClassAndColumn(Class<?> clazz) {
        boolean isEntityClass = clazz.equals(entrustWrapper.getEntityClass());
        List<ColumnSegment> columnSegmentList = entrustWrapper.getSegmentsByEnum(SegmentEnum.COLUMN);

        List<Field> clazzFields = null, fields = new ArrayList<>(columnSegmentList.size());
        for (ColumnSegment segment : columnSegmentList) {
            if (isEntityClass && segment.getColumnField() != null) {
                fields.add(segment.getColumnField());
                continue;
            }

            //获取结果属性名称,如无法匹配且ColumnName不为空,进行注解匹配.
            String tName = segment.generatorResFieldName();
            //处理第1梯度优先级(前3种情况)
            if (tName != null) {
                Field field = ReflectUtil.getField(clazz, tName);
                if (field != null) {
                    fields.add(field);
                    continue;
                }
            }
            //处理第2梯度优先级(第4种情况)
            if (StrUtil.isNotEmpty(segment.getColumnName())) {
                //懒加载
                clazzFields = clazzFields == null ? entrustWrapper.explainColumnFieldListByClass(clazz) : clazzFields;

                //遍历查找是否存在传入类的属性列表中是否存在 两类注解, 且列名一致
                Field tf = null;
                for (Field clzField : clazzFields) {
                    Column ca = clzField.getAnnotation(Column.class);
                    if (ca != null && segment.getColumnName().equals(ca.name())) {
                        tf = clzField;
                        break;
                    }
                    JoinColumn jca = clzField.getAnnotation(JoinColumn.class);
                    if (jca != null && segment.getColumnName().equals(jca.name())) {
                        tf = clzField;
                        break;
                    }
                }
                if (tf != null) {
                    fields.add(tf);
                }
            }
            //占位属性对象.
            fields.add(null);
        }
        return fields;
    }
}
